/**
 * 
 */
package org.dragonfire.dasm.directives;

import java.util.ArrayList;
import java.util.Stack;

import org.dragonfire.dasm.Command;
import org.dragonfire.dasm.Listing;
import org.dragonfire.dasm.DirectiveManager.Directive;
import org.dragonfire.parser.Equation;
import org.dragonfire.parser.functions.Function;

/**
 * Splits up a string on every match of a regular expression which can then be
 * accessed through the <code>$token()</code> function<br />
 * <br />
 * Format:<br />
 * <code>#TOKENIZE <i>&lt;text&gt;</i>,<i>&lt;regex&gt;</i></code><br />
 * <br />
 * Parameters:<br />
 * <table>
 * <tr>
 * <td><code>text</code></td>
 * <td>(String) The text to be split</td>
 * </tr>
 * <tr>
 * <td><code>regex</code></td>
 * <td>(String) The regular expression used to separate the <code>text</code></td>
 * </tr>
 * </table><br />
 * Errors:<br />
 * <table>
 * <tr>
 * <td><code>ERROR_STRING_REQUIRED</code></td>
 * <td>Thrown if <code>text</code> or <code>regex</code> are not strings.</td>
 * </tr>
 * <tr>
 * <td><code>ERROR_NOT_ENOUGH_ARGS</code></td>
 * <td>Thrown if <code>text</code> or <code>regex</code> are not defined.</td>
 * </tr>
 * </table>
 * 
 * @author Darth Android
 * @version 2.0
 * @see String#split(String)
 * @see org.dragonfire.dasm.directives.Tokenize.Token
 */
public class Tokenize extends Directive {

	/**
	 * Holds the token history with the most recent split at the top of the
	 * stack
	 */
	private Stack<String[]> tokenized = new Stack<String[]>();

	/**
	 * Default constructor, registers the <code>$token()</code> function
	 */
	public Tokenize() {
		// register function
		Function.putFunction("token", new Token(this));
	}

	@Override
	public ArrayList<Listing> pass1(Command c) {
		// get parameters
		String text = evalString(c.getParam(0));
		String regex = evalString(c.getParam(1));
		tokenized.push(text.split(regex));
		return null;
	}

	/**
	 * Returns the token list for a specific split
	 * 
	 * @param idx
	 *            The history index to retrieve, where 0 is the most recent set
	 *            of tokens and 1 is the next most recent set, etc.
	 * @return The token list for a specific split
	 */
	public String[] getTokenList(int idx) {
		return tokenized.get(tokenized.size() - 1 - idx);
	}

	/**
	 * Fetches a token from the <code>#{@link Tokenize}</code> directive.<br />
	 * <br />
	 * Format:<br />
	 * <code> $token(<i>&lt;index&gt;</i>[,<i>&lt;historyIndex&gt;</i>])</code><br />
	 * <br />
	 * Params:<br />
	 * <table>
	 * <tr>
	 * <td><code>index</code></td>
	 * <td>(Integer) The token index, with 0 returning the number of tokens and
	 * any other positive integer returning the <i>n</i>th token, or
	 * <code>""</code> if the token doesn't exist</td>
	 * </tr>
	 * <tr>
	 * <td><code>historyIndex</code></td>
	 * <td>(Integer) The history index, which specifies how many splits to go
	 * back; defaults to 0, the most recent split</td>
	 * </tr>
	 * </table>
	 * 
	 * @author Darth Android
	 * @version 2.0
	 * @see org.dragonfire.dasm.directives.Tokenize
	 */
	public static class Token extends Function {

		/**
		 * A reference to the {@link Tokenize} to retrieve history from.
		 */
		private Tokenize tokenizer;

		/**
		 * Creates a new <code>Token</code> function and links it with the
		 * specified {@link org.dragonfire.dasm.directives.Tokenize} directive.
		 * 
		 * @param tizer
		 *            The {@link org.dragonfire.dasm.directives.Tokenize} to
		 *            attach to
		 * @see org.dragonfire.dasm.directives.Tokenize
		 */
		public Token(Tokenize tizer) {
			tokenizer = tizer;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public String eval(Equation equ, String[] params) {
			// parse parameters, the token index and history index if it exists
			int index = equ.evalInt(params[0]);
			int history = 0;
			if (params.length > 1)
				history = equ.evalInt(params[1]);
			// Retrieve token list
			String[] parts = tokenizer.getTokenList(history);
			// return an empty string for invalid token indexes
			if (index < 0 || index > parts.length)
				return "\"\"";
			// return the number of tokens if token #0 is requested
			if (index == 0)
				return parts.length + "";
			// return the token
			return "\"" + parts[index - 1] + "\"";
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int minParams() {
			return 1;
		}
	}
}
